﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using EasyStep.Data.Entities;
using WPF.TestEditor.Properties;
using EasyStep.TestExecution.Interfaces;
using System.Threading;
using EasyStep.Business.Interfaces;
using EasyStep.TestExecution;
using EasyStep.TestExecution.Data;
using System.Diagnostics;

namespace WPF.TestEditor.Controls.Execute
{

    public partial class ExecutePanel : UserControlBase
    {
        public event EventHandler ExecutionCompleted;
        private const string DefaultTestName = "_ no name";
        public ExecutePanel()
            : base()
        {
            InitializeComponent();
            usrBrowsers.ExecuteTest += new EventHandler(usrBrowsers_ExecuteTest);
            usrBrowsers.StopTest += new EventHandler(usrBrowsers_StopTest);
        }

        

        public TestInfo Data { get; private set; }
        public void ReInit(TestInfo data, EasyStep.Business.Interfaces.IWebBrowserProxy[] startupBrowsers)
        {
            usrBrowsers.Selected = startupBrowsers;
            Data = data;
            if (Data == null)
                Data = TestDataContext.TestInfoManager.GetTest(TestDataContext.CurrentProject, TestDataContext.CurrentTest, true);
            Data.Name = string.IsNullOrEmpty(Data.Name) ? DefaultTestName : Data.Name;
            stackInfoPanel.Children.Clear();
            var i = 1;
            foreach (var ritem in Data.Steps)
            {
                ritem.OrderNr = i++;
                var rcontrol = new TestStepView(ritem);
                stackInfoPanel.Children.Add(rcontrol);
            }
            ShowSelectedTestInfo();
        }


        public void RunTest()
        {
            if (usrBrowsers.Selected == null || usrBrowsers.Selected.Length < 1)
            {
                MessageBox.Show("Error! At least one browser have to be selected!", "Can't execute test!", MessageBoxButton.OK, MessageBoxImage.Stop);
                return;
            }
            foreach(TestStepView item in stackInfoPanel.Children)
                item.ClearResults();
            var CurrentSessionName = string.Format("{0:yyyy_MM_dd HH_mm_ss}", DateTime.Now);
            var browsers = usrBrowsers.Selected;
            var context = TestDataContext;
            MainExecutionThread = new System.Threading.Thread(() =>
            {
                CurrentRunningThreads = new List<System.Threading.Thread>();
                foreach (var item in browsers)
                {
                    var executionInfo = new BrowserThreadData()
                        {
                            browser = item,
                            test = Data,
                            pauseLength = 5,
                            stopOnFirstFailed = Settings.Default.StopTestOnFirstError,
                            withScreenshots = Settings.Default.MakeScreenShotsAfterEachStep,
                            session = CurrentSessionName,
                            context = context
                        };
                    if (!Settings.Default.ExecuteTestContiniously)
                    {
                        CurrentRunningThreads.Add(new Thread(ExecuteTestsInBrowser) { Name = item.Name });
                        CurrentRunningThreads.Last().Start(executionInfo);
                    }
                    else
                        ExecuteTestsInBrowser(executionInfo);
                }
                if (Settings.Default.ExecuteTestContiniously)
                {
                    OnTestCompleted();
                }
            });
            MainExecutionThread.Start();
            SetControlsState();
        }

        private void OnTestCompleted()
        {
            UpateStatus("All completed\r\n\r\n");
            SetControlsState();
            this.Dispatcher.Invoke(new Action(()=>{
                if (ExecutionCompleted!=null)
                    ExecutionCompleted(this, new EventArgs());            
            }));
            SetControlsState();
        }

        private void ExecuteTestsInBrowser(object d)
        {
            var data = d as BrowserThreadData;
            var executionContext = new TestExecutionContext(data.context, data.browser, data.session);
            executionContext.CurrentTest = data.test.Name;//setup correct name
            var executor = new TestExecutor(executionContext);
            SetControlsState();

            executor.OnBeforeExecuteStep += new TestStepEventHandler(executor_OnBeforeExecuteStep);
            executor.OnAfterExecuteStep += new TestStepEventHandler(executor_OnAfterExecuteStep);
            executor.OnExecutionCompleted += new EventHandler(executor_OnExecutionCompleted);
            executor.OnBeforeExecuteChildStep += new TestStepEventHandler(executor_OnBeforeExecuteChildStep);
            executor.OnAfterExecuteChildStep += new TestStepEventHandler(executor_OnAfterExecuteChildStep);
            try
            {
                UpateStatus("=!=Started: {2}", new object[] { System.IO.Path.GetDirectoryName(executionContext.CurrentProject.TrimEnd('\\')), executionContext.CurrentTest, data.browser.Name, data.session });
                executor.Run(data.test, data.withScreenshots, data.stopOnFirstFailed, data.pauseLength);
            }
            catch (Exception ex)
            {
                UpateStatus("Error in browser {1}!\r\n{0}\r\n check log file...", new object[] { data.browser.Name, ex.Message });
                data.context.Log.Fatal("Test Execution Error!", ex);
                executor.Dispose();
                SetControlsState();
            }
            SetControlsState();
        }

        private void UpateStatus(string message, params object[] formatParams)
        {

            if (formatParams != null)
                message = string.Format(message, formatParams);
            this.Dispatcher.Invoke(new Action(() =>
            {
                txtTotalLog.Text += "\r\n" + message;
                TestDataContext.Log.Debug(message);
            }));

        }

        private bool SetControlsState()
        {
            var isWorking = false;
            this.Dispatcher.Invoke(new Action(() =>
            {
                isWorking = Settings.Default.ExecuteTestContiniously && MainExecutionThread !=null && MainExecutionThread.IsAlive;
                isWorking = isWorking || (!Settings.Default.ExecuteTestContiniously && CurrentRunningThreads!=null && CurrentRunningThreads.Count > 0);
                
                this.usrBrowsers.SetState(isWorking);
            }));

            return isWorking;
        }
        #region [ Events ]
        void executor_OnBeforeExecuteStep(ITestExecutor executor, ExecuteEventArgs e)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                UpateStatus(" + started: {0}, {1}", new object[] { e.TestStepResult.Browser, executor.DataContext.TestStepManager.Convert(e.TestStepResult.StepInfo) });
                FindAndUpdateBrowserResult(e.TestStepResult);
            }));
        }
        void executor_OnBeforeExecuteChildStep(ITestExecutor executor, ExecuteEventArgs e)
        {
            string location = "";
            TestStepResult topmost;
            int level = 0;
            GetSubStepInfo(e, out location, out topmost, out level);
            UpateStatus(" {0} started:{3},{1}, {2}, path: {4}", new object[] { new string('+', level), e.TestStepResult.Browser, executor.DataContext.TestStepManager.Convert(e.TestStepResult.StepInfo), e.TestStepResult.Result, location });
            if (topmost != null)
                this.Dispatcher.Invoke(new Action(() =>
                {
                    topmost.Result = e.TestStepResult.Result;
                    FindAndUpdateBrowserResult(topmost);
                }));
        }

        void executor_OnAfterExecuteStep(ITestExecutor executor, ExecuteEventArgs e)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                UpateStatus(" - completed:{2}-{0}, {1}", new object[] { e.TestStepResult.Browser, executor.DataContext.TestStepManager.Convert(e.TestStepResult.StepInfo), e.TestStepResult.Result });
                FindAndUpdateBrowserResult(e.TestStepResult);
            }));
        }


        void executor_OnAfterExecuteChildStep(ITestExecutor executor, ExecuteEventArgs e)
        {
            string location = "";
            TestStepResult topmost;
            int level= 0;
            GetSubStepInfo(e, out location, out topmost, out level);
            UpateStatus(" {0} completed:{3},{1}, {2}, path: {4}", new object[] {new string('-',level), e.TestStepResult.Browser, executor.DataContext.TestStepManager.Convert(e.TestStepResult.StepInfo), e.TestStepResult.Result, location});
            if (topmost!=null)
                this.Dispatcher.Invoke(new Action(() =>
                {
                    topmost.Result = e.TestStepResult.Result;
                    FindAndUpdateBrowserResult(topmost);
                }));
        }

        private static void GetSubStepInfo(ExecuteEventArgs e, out string location, out TestStepResult topmost, out int level)
        {
            var path = new StringBuilder();
            var parent = e.TestStepResult.Parent;
            topmost = parent;
            path.AppendFormat("left:{0}", parent.Children.Count() - e.TestStepResult.StepInfo.OrderNr);
            var i = 1;
            while (parent != null)
            {
                path.AppendFormat("\t <{0}:[{2}{1}]", parent.StepInfo.OrderNr, parent.StepInfo.Action.Name, parent.StepInfo.Action.Parent != null ? parent.StepInfo.Action.Parent.Name : "");
                if (parent.Parent != null)
                {
                    topmost = parent.Parent;
                    parent = topmost;
                }
                else
                    parent = parent.Parent;
                i++;
            }
            level = i;
            location = path.ToString();
        }
        void executor_OnExecutionCompleted(object sender, EventArgs e)
        {
            var executor = sender as TestExecutor;
            UpateStatus("=!=finished: {0}\r\n", new object[] { executor.DataContext.BrowserInfo.Name });
            var ch = CurrentRunningThreads.Where(th => th.Name.Equals(executor.DataContext.BrowserInfo.Name)).FirstOrDefault();
            if (ch != null) CurrentRunningThreads.Remove(ch);
            var isworking=SetControlsState();
            if (!isworking)
                OnTestCompleted();
        }

        private void FindAndUpdateBrowserResult(TestStepResult data)
        {           
            var stepBrowser = stackInfoPanel.Children.OfType<TestStepView>().Where(tv => tv.Id.Equals(data.StepInfo.Id)).FirstOrDefault();
            if (stepBrowser == null)
            {
                TestDataContext.Log.Fatal("Can't find control by name to render test step result(FindAndUpdateBrowserResult(TestStepResult data):" + data.StepInfo.Id);
                return;
            }
            stepBrowser.SetResult(data);
        }
        #endregion
        public System.Threading.Thread MainExecutionThread { get; private set; }
        public List<System.Threading.Thread> CurrentRunningThreads { get; private set; }
        void usrBrowsers_ExecuteTest(object sender, EventArgs e)
        {
            RunTest();
        }
        void usrBrowsers_StopTest(object sender, EventArgs e)
        {
            if (CurrentRunningThreads!=null && CurrentRunningThreads.Count > 0)
            {
                CurrentRunningThreads.ForEach(t => t.Abort());
                CurrentRunningThreads = null;
            }

            if (MainExecutionThread != null)
            {
                MainExecutionThread.Abort();
                MainExecutionThread = null;
            }
            SetControlsState();
        }

        private class BrowserThreadData
        {
            public IWebBrowserProxy browser;
            public TestInfo test;
            public bool stopOnFirstFailed;
            public bool withScreenshots;
            public int pauseLength;
            public string session;
            public ITestDataContext context;
        }

        private void ShowSelectedTestInfo(string testname = null)
        {
            string curEnvironment = !string.IsNullOrEmpty(TestDataContext.Environment) ? " [" + TestDataContext.Environment + "]" : "";
            string curProj = System.IO.Path.GetFileName(TestDataContext.CurrentProject.TrimEnd('\\'));
            string curTest = testname != null ? testname : !string.IsNullOrEmpty(Data.Name) ? Data.Name : DefaultTestName;
            TestDataContext.Log.Debug(string.Format("initializing test to execute\\ cur environment:{0} \\cur project: {1} \\cur test: {2}", curEnvironment, curProj, curTest));
            txtTestInfo.Text = string.Format("{0}\\{1}\\{2}", curEnvironment, curProj, curTest);
        }
    }



}
